#ifndef __TOOL_H__
#define __TOOL_H__
#include <cfloat>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <time.h>
#include <omp.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <opencv2/imgproc/imgproc.hpp>
#include "opencv2/flann/flann.hpp"
#include "boost/algorithm/string.hpp"
#include "google/protobuf/text_format.h"

#include "caffe/caffe.hpp"

#include "ex.h"

#include <faiss/IndexFlat.h>
#include <faiss/index_io.h>

#define GPU_NUM 2


using namespace cv;
using namespace std;
using namespace caffe;

const int TOPK = 1;
const string Mode = "CPU";


string line_to_line(string line)
{
        std::size_t pos = line.find(' ');
        return line.substr( 0 ,pos );
}

string name_to_name(string name)
{
        std::size_t pos = name.rfind('/');
        return name.substr(pos + 1);
}

string line_after_line(string line)
{
        std::size_t pos = line.rfind(' ');
        return line.substr(pos+1);
}

Mat extract_feat_not_saved(const char* file_list, vector<bool> &is_image,string Mode, int device_id = 0)
{
	if(Mode == "GPU")
	{
		Caffe::SetDevice(device_id);
        	Caffe::set_mode(Caffe::GPU);
	}
	else if(Mode == "CPU")
	{
		Caffe::set_mode(Caffe::CPU);
	}
	//zwb/ cout << "start extact feature.." << endl;

	string layer_name = "fc7";
        string deploy_file = "./fast_deploy.prototxt";
        string weight_file = "./fast_net.caffemodel";
	boost::shared_ptr<Net<float> > caffe_test_net(
		new Net<float>(deploy_file, caffe::TEST) );
	caffe_test_net->CopyTrainedLayersFrom(weight_file);

	const float mean_acquired[3] = {104, 117, 123}; 

	ifstream infile(file_list);
  	vector<string> image_files;
  	string line;
  	while(getline(infile, line))
	{
		line = line_to_line(line);
		//zwb/ cout << line << endl;
    		image_files.push_back(line);
	}

	const int img_num = image_files.size();
    const int feat_dim = caffe_test_net->blob_by_name(layer_name)->channels();
	Mat feat_mat = Mat::zeros(img_num, feat_dim, CV_32FC1);
	

	cv::Mat cv_img, cv_img_orig;
  	int n_channel = 3;
  	int image_size = 256;
  	int image_crop_size = 227;
  	int n_image_crop_pixel = n_channel*image_crop_size*image_crop_size;
  	int height_offset = 14;
  	int width_offset = 14;	

	int batch_size = caffe_test_net->blob_by_name("data")->num();
  	n_channel = caffe_test_net->blob_by_name("data")->channels();
	image_crop_size = caffe_test_net->blob_by_name("data")->height();
	n_image_crop_pixel = n_channel*image_crop_size*image_crop_size;
	height_offset = (image_size - image_crop_size) / 2;
	width_offset = (image_size - image_crop_size) / 2;


	vector<string> batch_image_names;
	vector<Blob<float>*> batch_image_blobs;
  	Blob<float>* image_blob = new Blob<float>(batch_size, n_channel, image_crop_size, image_crop_size);
	
	for (int image_id = 0; image_id < image_files.size(); ++image_id) 
	{
    		batch_image_names.push_back(image_files[image_id]);

    		cv_img_orig = cv::imread(image_files[image_id], CV_LOAD_IMAGE_COLOR);
		if(!cv_img_orig.data)
		{
			string command = "python gif2jpg.py " + image_files[image_id];
			system(command.c_str());

			cv_img_orig = cv::imread("./foo0.jpg", CV_LOAD_IMAGE_COLOR);
			if(!cv_img_orig.data)
			{
				is_image.push_back(false);
				cv_img = Mat::ones(image_size, image_size, CV_8UC3);
			}
			else{
				cv::resize(cv_img_orig, cv_img, cv::Size(image_size, image_size));
                        	is_image.push_back(true);
			}
		}
		else
    		{
			cv::resize(cv_img_orig, cv_img, cv::Size(image_size, image_size));
			is_image.push_back(true);
		}

    		float* image_blob_data = image_blob->mutable_cpu_data();
    		for (int i_channel = 0; i_channel < n_channel; ++i_channel) {
      		for (int height = 0; height < image_crop_size; ++height) {
        	for (int width = 0; width < image_crop_size; ++width) {
          		int insertion_index = (image_id % batch_size)* n_image_crop_pixel + (i_channel * image_crop_size + height) * image_crop_size + width;
          		int mean_index = (i_channel * image_size + height + height_offset)*image_size + width + width_offset;
          		image_blob_data[insertion_index] = (static_cast<float>(cv_img.at<cv::Vec3b>(height + height_offset, width + width_offset)[i_channel]) - mean_acquired[i_channel]);
        	}
      		}
    		}

    		if ((image_id + 1) % batch_size == 0 || (image_id + 1) == image_files.size()) {
      			batch_image_blobs.push_back(image_blob);
      			const vector<Blob<float>*>& result = caffe_test_net->Forward(batch_image_blobs);

      			const int n_example = batch_image_names.size();
        			
			const boost::shared_ptr<Blob<float> > data_blob = caffe_test_net->blob_by_name(layer_name);
        		const float* data_blob_ptr = data_blob->cpu_data();
        		const int n_channel = data_blob->channels();
			//fwrite(data_blob_ptr, sizeof(float), n_example * n_channel, output_file);
			float *temp_data = new float[n_example * n_channel];
			for(int t = 0; t < n_example * n_channel; t++)
				temp_data[t] = data_blob_ptr[t];
			Mat batch_feat(n_example, n_channel, CV_32FC1, temp_data);
			batch_feat.copyTo(feat_mat.rowRange(image_id + 1 - n_example, image_id + 1));
			delete temp_data;

      			//zwb/ LOG(INFO) << image_id + 1 << " (+" << n_example
        			//zwb/ << ") out of " << image_files.size() << " results written.";

      			batch_image_names.clear();
     			batch_image_blobs.clear();
    		}
  	}

	delete image_blob;
	return feat_mat;	
}

Mat extract_feat_not_saved_net(vector<string> image_files,Net<float>* caffe_test_net,vector<bool> &is_image, int device_id = 0)
{
        /*if(Mode == "GPU")
        {
                Caffe::SetDevice(device_id);
                Caffe::set_mode(Caffe::GPU);
        }
        else if(Mode == "CPU")
        {
                Caffe::set_mode(Caffe::CPU);
        }
        //zwb/ cout << "start extact feature.." << endl;

        //string layer_name = "fc7";
        string deploy_file = "/home/ubuntu/deepDemo/fast_deploy.prototxt";
        string weight_file = "/home/ubuntu/deepDemo/fast_net.caffemodel";
        boost::shared_ptr<Net<float> > caffe_test_net(
                new Net<float>(deploy_file, caffe::TEST) );
        caffe_test_net->CopyTrainedLayersFrom(weight_file);
*/
        
        const float mean_acquired[3] = {104, 117, 123};

        string layer_name = "fc7";
        const int img_num = image_files.size();
		//cout<<"ok"<<endl;
    const int feat_dim = caffe_test_net->blob_by_name(layer_name)->channels();
        Mat feat_mat = Mat::zeros(img_num, feat_dim, CV_32FC1);

		//cout<<"ok1"<<endl;
        cv::Mat cv_img, cv_img_orig;
        int n_channel = 3;
        int image_size = 256;
        int image_crop_size = 227;
        int n_image_crop_pixel = n_channel*image_crop_size*image_crop_size;
        int height_offset = 14;
        int width_offset = 14;

        int batch_size = caffe_test_net->blob_by_name("data")->num();
        n_channel = caffe_test_net->blob_by_name("data")->channels();
        cout<<"n_channel:"<<n_channel<<endl;
        image_crop_size = caffe_test_net->blob_by_name("data")->height();
        n_image_crop_pixel = n_channel*image_crop_size*image_crop_size;
        height_offset = (image_size - image_crop_size) / 2;
        width_offset = (image_size - image_crop_size) / 2;


        vector<string> batch_image_names;
        vector<Blob<float>*> batch_image_blobs;
        Blob<float>* image_blob = new Blob<float>(batch_size, n_channel, image_crop_size, image_crop_size);

        for (int image_id = 0; image_id < image_files.size(); ++image_id)
        {
                batch_image_names.push_back(image_files[image_id]);
                cout<<image_files[image_id]<<endl;

                cv_img_orig = cv::imread(image_files[image_id], CV_LOAD_IMAGE_COLOR);
                if(!cv_img_orig.data)
                {
                        string command = "python gif2jpg.py " + image_files[image_id];
                        system(command.c_str());

                        cv_img_orig = cv::imread("./foo0.jpg", CV_LOAD_IMAGE_COLOR);
                        if(!cv_img_orig.data)
                        {
                                is_image.push_back(false);
                                cv_img = Mat::ones(image_size, image_size, CV_8UC3);
                        }
                        else{
                                cv::resize(cv_img_orig, cv_img, cv::Size(image_size, image_size));
                                is_image.push_back(true);
                        }
                }
                else
                {
                        cv::resize(cv_img_orig, cv_img, cv::Size(image_size, image_size));
                        is_image.push_back(true);
                }

                float* image_blob_data = image_blob->mutable_cpu_data();
                for (int i_channel = 0; i_channel < n_channel; ++i_channel) {
                for (int height = 0; height < image_crop_size; ++height) {
                for (int width = 0; width < image_crop_size; ++width) {
                        int insertion_index = (image_id % batch_size)* n_image_crop_pixel + (i_channel * image_crop_size + height) * image_crop_size + width;
                        int mean_index = (i_channel * image_size + height + height_offset)*image_size + width + width_offset;
                        image_blob_data[insertion_index] = (static_cast<float>(cv_img.at<cv::Vec3b>(height + height_offset, width + width_offset)[i_channel]) - mean_acquired[i_channel]);
                }
                }
                }

                if ((image_id + 1) % batch_size == 0 || (image_id + 1) == image_files.size()) {
                        batch_image_blobs.push_back(image_blob);
                        const vector<Blob<float>*>& result = caffe_test_net->Forward(batch_image_blobs);
                        const int n_example = batch_image_names.size();

                        const boost::shared_ptr<Blob<float> > data_blob = caffe_test_net->blob_by_name(layer_name);
                        const float* data_blob_ptr = data_blob->cpu_data();

                        const int n_channel = data_blob->channels();

                        //fwrite(data_blob_ptr, sizeof(float), n_example * n_channel, output_file);
                        float *temp_data = new float[n_example * n_channel];
                        for(int t = 0; t < n_example * n_channel; t++)
                        {       temp_data[t] = data_blob_ptr[t];
                        //       cout<<data_blob_ptr[t] ;
                }
                        Mat batch_feat(n_example, n_channel, CV_32FC1, temp_data);
                        batch_feat.copyTo(feat_mat.rowRange(image_id + 1 - n_example, image_id + 1));
                        delete []temp_data;

                        //zwb/ LOG(INFO) << image_id + 1 << " (+" << n_example
                                //zwb/ << ") out of " << image_files.size() << " results written.";

                        batch_image_names.clear();
                        batch_image_blobs.clear();
                }
        }

        delete image_blob;
        return feat_mat;
}
Mat extract_feat_not_saved_net(vector<string> image_files,boost::shared_ptr<Net<float> > caffe_test_net,vector<bool> &is_image, int device_id = 0)
{
        /*if(Mode == "GPU")
        {
                Caffe::SetDevice(device_id);
                Caffe::set_mode(Caffe::GPU);
        }
        else if(Mode == "CPU")
        {
                Caffe::set_mode(Caffe::CPU);
        }
        //zwb/ cout << "start extact feature.." << endl;

        //string layer_name = "fc7";
        string deploy_file = "/home/ubuntu/deepDemo/fast_deploy.prototxt";
        string weight_file = "/home/ubuntu/deepDemo/fast_net.caffemodel";
        boost::shared_ptr<Net<float> > caffe_test_net(
                new Net<float>(deploy_file, caffe::TEST) );
        caffe_test_net->CopyTrainedLayersFrom(weight_file);
*/
        
        const float mean_acquired[3] = {104, 117, 123};

        string layer_name = "fc7";
        const int img_num = image_files.size();
		cout<<"ok"<<endl;
    const int feat_dim = caffe_test_net->blob_by_name(layer_name)->channels();
        Mat feat_mat = Mat::zeros(img_num, feat_dim, CV_32FC1);

		cout<<"ok1"<<endl;
        cv::Mat cv_img, cv_img_orig;
        int n_channel = 3;
        int image_size = 256;
        int image_crop_size = 227;
        int n_image_crop_pixel = n_channel*image_crop_size*image_crop_size;
        int height_offset = 14;
        int width_offset = 14;

        int batch_size = caffe_test_net->blob_by_name("data")->num();
        n_channel = caffe_test_net->blob_by_name("data")->channels();
        cout<<"n_channel:"<<n_channel<<endl;
        image_crop_size = caffe_test_net->blob_by_name("data")->height();
        n_image_crop_pixel = n_channel*image_crop_size*image_crop_size;
        height_offset = (image_size - image_crop_size) / 2;
        width_offset = (image_size - image_crop_size) / 2;


        vector<string> batch_image_names;
        vector<Blob<float>*> batch_image_blobs;
        Blob<float>* image_blob = new Blob<float>(batch_size, n_channel, image_crop_size, image_crop_size);

        for (int image_id = 0; image_id < image_files.size(); ++image_id)
        {
                batch_image_names.push_back(image_files[image_id]);
                cout<<image_files[image_id]<<endl;

                cv_img_orig = cv::imread(image_files[image_id], CV_LOAD_IMAGE_COLOR);
                if(!cv_img_orig.data)
                {
                        string command = "python gif2jpg.py " + image_files[image_id];
                        system(command.c_str());

                        cv_img_orig = cv::imread("./foo0.jpg", CV_LOAD_IMAGE_COLOR);
                        if(!cv_img_orig.data)
                        {
                                is_image.push_back(false);
                                cv_img = Mat::ones(image_size, image_size, CV_8UC3);
								cout<<cv_img<<endl;
                        }
                        else{
                                cv::resize(cv_img_orig, cv_img, cv::Size(image_size, image_size));
                                is_image.push_back(true);
                        }
                }
                else
                {
                        cv::resize(cv_img_orig, cv_img, cv::Size(image_size, image_size));
                        is_image.push_back(true);
                }

                float* image_blob_data = image_blob->mutable_cpu_data();
                for (int i_channel = 0; i_channel < n_channel; ++i_channel) {
                for (int height = 0; height < image_crop_size; ++height) {
                for (int width = 0; width < image_crop_size; ++width) {
                        int insertion_index = (image_id % batch_size)* n_image_crop_pixel + (i_channel * image_crop_size + height) * image_crop_size + width;
                        int mean_index = (i_channel * image_size + height + height_offset)*image_size + width + width_offset;
                        image_blob_data[insertion_index] = (static_cast<float>(cv_img.at<cv::Vec3b>(height + height_offset, width + width_offset)[i_channel]) - mean_acquired[i_channel]);
                }
                }
                }

                if ((image_id + 1) % batch_size == 0 || (image_id + 1) == image_files.size()) {
                        batch_image_blobs.push_back(image_blob);
                        const vector<Blob<float>*>& result = caffe_test_net->Forward(batch_image_blobs);
                        const int n_example = batch_image_names.size();

                        const boost::shared_ptr<Blob<float> > data_blob = caffe_test_net->blob_by_name(layer_name);
                        const float* data_blob_ptr = data_blob->cpu_data();

                        const int n_channel = data_blob->channels();

                        //fwrite(data_blob_ptr, sizeof(float), n_example * n_channel, output_file);
                        float *temp_data = new float[n_example * n_channel];
                        for(int t = 0; t < n_example * n_channel; t++)
                        {       temp_data[t] = data_blob_ptr[t];
                        //       cout<<data_blob_ptr[t] ;
                }
                        Mat batch_feat(n_example, n_channel, CV_32FC1, temp_data);
                        batch_feat.copyTo(feat_mat.rowRange(image_id + 1 - n_example, image_id + 1));
                        delete []temp_data;

                        //zwb/ LOG(INFO) << image_id + 1 << " (+" << n_example
                                //zwb/ << ") out of " << image_files.size() << " results written.";

                        batch_image_names.clear();
                        batch_image_blobs.clear();
                }
        }

        delete image_blob;
        return feat_mat;
}



void extract_feat_saved(const char* file_list, string output_file_name, string Mode, int device_id = 0, bool append_feature = false)
{

	if(Mode == "GPU"){
        Caffe::SetDevice(device_id);
        Caffe::set_mode(Caffe::GPU);
    }
    else if(Mode == "CPU"){
        Caffe::set_mode(Caffe::CPU);
    }
	//zwb/ cout << "start extact feature.." << endl;

	string layer_name = "fc7";
    string deploy_file = "./data/fast_deploy.prototxt";
    string weight_file = "./data/fast_net.caffemodel";
	boost::shared_ptr<Net<float> > caffe_test_net(new Net<float>(deploy_file, caffe::TEST) );
	caffe_test_net->CopyTrainedLayersFrom(weight_file);

	const float mean_acquired[3] = {104, 117, 123}; 

	ifstream infile(file_list);
  	vector<string> image_files;
  	string line;
  	while(getline(infile, line))
	{
		line = line_to_line(line);
		//zwb/ cout << line << endl;
        image_files.push_back(line);
	}

	FILE *output_file;// = fopen(output_file_name.c_str(), "wb");
	if(!append_feature)
		output_file = fopen(output_file_name.c_str(), "wb");
	else 	
		output_file = fopen(output_file_name.c_str(), "ab+");

	const int img_num = image_files.size();
    const int feat_dim = caffe_test_net->blob_by_name(layer_name)->channels();
	if(!append_feature)
	{
	    	fwrite(&img_num, sizeof(int), 1, output_file);
    		fwrite(&feat_dim, sizeof(int), 1, output_file);
	}


	cv::Mat cv_img, cv_img_orig;
  	int n_channel = 3;
  	int image_size = 256;
  	int image_crop_size = 227;
  	int n_image_crop_pixel = n_channel*image_crop_size*image_crop_size;
  	int height_offset = 14;
  	int width_offset = 14;	

	int batch_size = caffe_test_net->blob_by_name("data")->num();
  	n_channel = caffe_test_net->blob_by_name("data")->channels();
	image_crop_size = caffe_test_net->blob_by_name("data")->height();
	n_image_crop_pixel = n_channel*image_crop_size*image_crop_size;
	height_offset = (image_size - image_crop_size) / 2;
	width_offset = (image_size - image_crop_size) / 2;


	vector<string> batch_image_names;
	vector<Blob<float>*> batch_image_blobs;
  	Blob<float>* image_blob = new Blob<float>(batch_size, n_channel, image_crop_size, image_crop_size);
	
	for (int image_id = 0; image_id < image_files.size(); ++image_id) 
	{
    	batch_image_names.push_back(image_files[image_id]);

    	cv_img_orig = cv::imread(image_files[image_id], CV_LOAD_IMAGE_COLOR);
		if(!cv_img_orig.data){


			string command = "python gif2jpg.py " + image_files[image_id];
			system(command.c_str());
            cv_img_orig = cv::imread("./foo0.jpg", CV_LOAD_IMAGE_COLOR);
            if(!cv_img_orig.data){
                                //is_image.push_back(false);
                cv_img = Mat(image_size, image_size, CV_8UC3);
				randu(cv_img, Scalar::all(0), Scalar::all(255));
                                //cv_img = Mat::ones(image_size, image_size, CV_8UC3);
            }
            else{
                cv::resize(cv_img_orig, cv_img, cv::Size(image_size, image_size));
                               // is_image.push_back(true);
            }

		}
		else
    	    cv::resize(cv_img_orig, cv_img, cv::Size(image_size, image_size));

    	float* image_blob_data = image_blob->mutable_cpu_data();
    	for (int i_channel = 0; i_channel < n_channel; ++i_channel) {
      		for (int height = 0; height < image_crop_size; ++height) {
        	    for (int width = 0; width < image_crop_size; ++width) {
          		    int insertion_index = (image_id % batch_size)* n_image_crop_pixel + (i_channel * image_crop_size + height) * image_crop_size + width;
          		    int mean_index = (i_channel * image_size + height + height_offset)*image_size + width + width_offset;
          		    image_blob_data[insertion_index] = (static_cast<float>(cv_img.at<cv::Vec3b>(height + height_offset, width + width_offset)[i_channel]) - mean_acquired[i_channel]);
        	    }
      		}
    	}

    	if ((image_id + 1) % batch_size == 0 || (image_id + 1) == image_files.size()) {
      	    batch_image_blobs.push_back(image_blob);
      		const vector<Blob<float>*>& result = caffe_test_net->Forward(batch_image_blobs);

      		const int n_example = batch_image_names.size();
        			
			const boost::shared_ptr<Blob<float> > data_blob = caffe_test_net->blob_by_name(layer_name);
        	const float* data_blob_ptr = data_blob->cpu_data();
        	const int n_channel = data_blob->channels();
			fwrite(data_blob_ptr, sizeof(float), n_example * n_channel, output_file);
		
      			//zwb/ LOG(INFO) << image_id + 1 << " (+" << n_example
        		//zwb/ 	<< ") out of " << image_files.size() << " results written.";

      		batch_image_names.clear();
     		batch_image_blobs.clear();
    	}
  	}

	fclose(output_file);
	delete image_blob;
}

void cosine_retrival(vector<bool> is_image,vector<string> query_name, vector<string> retrival_name, int queryNum, int retrivalNum, Mat feat_query, Mat feat_retrival, float threshold)
{
#ifndef _OPENMP
        //cout<<"OpenMP not supported"<<endl;
#endif

		time_t t3 = time(NULL);
		Mat scores = Mat::zeros(queryNum, retrivalNum, CV_32F);
		const int batch = 5;
                #pragma omp parallel for
                for (int  i = 0; i <= queryNum - batch; i += batch)
                {
                        Mat A_row = feat_query.rowRange(i, i + batch).clone();
                        Mat D_row = A_row* (feat_retrival.t());
                        D_row.copyTo(scores.rowRange(i, i + batch));
						
                }
                if(queryNum % batch != 0)
                {
                        int t = queryNum % batch;
                        Mat A_row = feat_query.rowRange(queryNum - t, queryNum).clone();
                        Mat D_row = A_row* (feat_retrival.t());
                        D_row.copyTo(scores.rowRange(queryNum - t, queryNum));
                }
                time_t t4 = time(NULL);
                //zwb/ cout << "parallel matrix multiplication, time: " << (t4- t3) << " seconds."<< endl;

                //zwb/ cout << "your threshold is: " << threshold << endl;
                Mat index;
                sortIdx(scores, index, SORT_EVERY_ROW + SORT_DESCENDING);
                for(int i = 0; i < queryNum; i++)
                {
                        //zwb/ cout << endl << "query image id: "<< i << ", name: " << query_name[i] << endl;
                         
                        for(int j = 0; j < TOPK; j++)
                        {
                                int t = index.at<int>(i, j);
				if (retrival_name[t] == "43c9c4b57e6c7e1638438c722b7fa927" || retrival_name[t] == "47b9fbd0d7258bbf45cc6c2fb9cd61e0" || retrival_name[t] == "8047b4e3cc6856335d15a4f96cd2e6a7" || retrival_name[t] == "982bcb887525dec8e8f84bdf08fd3b9f" || retrival_name[t] == "91b19e7d8c61cf0ae2ad97d1b7a962c4"|| retrival_name[t] == "e6cd78f4511152c9bc89ca185374ca7b" || retrival_name[t] == "e0fc1347f0245e75266177becc99b47c" || retrival_name[t] == "e5f3b10fe7a6e41dcae9d7faf571637c" || retrival_name[t] == "e09fcac5602b45916eb87602daf27c54" || retrival_name[t] == "c670e9e675272142e8c0ad9613e66192" || retrival_name[t] == "de03657e1c139cc39816a43704dd2932" || retrival_name[t] == "4fabfc3b2f28d1b6f8c3b617d617d249" || retrival_name[t] == "6e38d392766e64f8f75edde0c6776c98" || retrival_name[t] == "e4bb7fed959444c4d0b297ac21355f27" || retrival_name[t] == "874669bc3a775a0db66055250448ee8e" || retrival_name[t] == "9c49ef9bfad450c34ff61bf08113d3b0")
				{
					//threshold=0.97;
                                	if(scores.at<float>(i,t) > 0.93 && is_image[i])
					//{
                                	//zwb/ cout << "similar image id: " << t <<", name: " << retrival_name[t]<<" ";
                                	cout << i << " " << retrival_name[t]<<" "<< scores.at<float>(i,t) << endl;
                      			 
					//}
				}
				

				else if(retrival_name[t] == "f2ac2484802d16ad761226c8db5f3a6d" || retrival_name[t] == "31be1bb7daedbd2098be93777da4e80a" || retrival_name[t] == "a1aac9e96c5c49dfabb6be5f428d100d" || retrival_name[t] == "eb886f4a465de7a32d7f8e353a73f99c" || retrival_name[t]== "a25967217571e914a97449b4fbeef812" || retrival_name[t] == "a1aac9e96c5c49dfabb6be5f428d100d" || retrival_name[t] == "252873714b9a08cdba77e27695707f38" || retrival_name[t] == "764115d430662f7943eaaec7db12dc3e" || retrival_name[t] == "11be01413909989811131a0193894914" || retrival_name[t] =="12488ec380d041534f82dd6cfeebccdf" || retrival_name[t] == "9f71e451fd044385478d2109ccb245e6" || retrival_name[t] == "b32a1a5cb98f07e3fb7c7c2542ed550d" || retrival_name[t] == "1d1339e7ee191ec94442595a5a5211e1" || retrival_name[t] == "26b69573ba74e9803319913f80171c61" || retrival_name[t] == "826e0ed562c5e1b842065ea59aef046b")
				{	
					if(scores.at<float>(i,t) > 0.93 && is_image[i])
					cout << i << " " << retrival_name[t]<<" "<< scores.at<float>(i,t) << endl;
				}	

				else
				{
					if (scores.at<float>(i,t) > threshold && is_image[i])
					//{
					cout << i << " " << retrival_name[t]<<" "<< scores.at<float>(i,t) << endl;
					//}		
				}
			 }
                     
                }
                time_t t6 = time(NULL);
				vector<bool>().swap(is_image);
				scores.release();
				index.release();
                //zwb/ cout << "show results done, time: " << (t6- t4) << " seconds."<< endl;		
}

string cosine_retrival_return(vector<bool> is_image,vector<string> query_name, vector<string> retrival_name, int queryNum, int retrivalNum, Mat feat_query, Mat feat_retrival, float threshold)
{
#ifndef _OPENMP
        //cout<<"OpenMP not supported"<<endl;
#endif
        stringstream ss;
		time_t t3 = time(NULL);
		Mat scores = Mat::zeros(queryNum, retrivalNum, CV_32F);
		const int batch = 5;
                #pragma omp parallel for
                for (int  i = 0; i <= queryNum - batch; i += batch)
                {
                        Mat A_row = feat_query.rowRange(i, i + batch).clone();
                        Mat D_row = A_row* (feat_retrival.t());
                        D_row.copyTo(scores.rowRange(i, i + batch));
						
                }
                if(queryNum % batch != 0)
                {
                        int t = queryNum % batch;
                        Mat A_row = feat_query.rowRange(queryNum - t, queryNum).clone();
                        Mat D_row = A_row* (feat_retrival.t());
                        D_row.copyTo(scores.rowRange(queryNum - t, queryNum));
                }
                time_t t4 = time(NULL);
                //zwb/ cout << "parallel matrix multiplication, time: " << (t4- t3) << " seconds."<< endl;

                //zwb/ cout << "your threshold is: " << threshold << endl;
                Mat index;
                sortIdx(scores, index, SORT_EVERY_ROW + SORT_DESCENDING);
                for(int i = 0; i < queryNum; i++)
                {
                        //zwb/ cout << endl << "query image id: "<< i << ", name: " << query_name[i] << endl;
                         
                        for(int j = 0; j < TOPK; j++)
                        {
                                int t = index.at<int>(i, j);
				if (retrival_name[t] == "43c9c4b57e6c7e1638438c722b7fa927" || retrival_name[t] == "47b9fbd0d7258bbf45cc6c2fb9cd61e0" || retrival_name[t] == "8047b4e3cc6856335d15a4f96cd2e6a7" || retrival_name[t] == "982bcb887525dec8e8f84bdf08fd3b9f" || retrival_name[t] == "91b19e7d8c61cf0ae2ad97d1b7a962c4"|| retrival_name[t] == "e6cd78f4511152c9bc89ca185374ca7b" || retrival_name[t] == "e0fc1347f0245e75266177becc99b47c" || retrival_name[t] == "e5f3b10fe7a6e41dcae9d7faf571637c" || retrival_name[t] == "e09fcac5602b45916eb87602daf27c54" || retrival_name[t] == "c670e9e675272142e8c0ad9613e66192" || retrival_name[t] == "de03657e1c139cc39816a43704dd2932" || retrival_name[t] == "4fabfc3b2f28d1b6f8c3b617d617d249" || retrival_name[t] == "6e38d392766e64f8f75edde0c6776c98" || retrival_name[t] == "e4bb7fed959444c4d0b297ac21355f27" || retrival_name[t] == "874669bc3a775a0db66055250448ee8e" || retrival_name[t] == "9c49ef9bfad450c34ff61bf08113d3b0")
				{
					//threshold=0.97;
                                	if(scores.at<float>(i,t) > 0.93 && is_image[i])
					//{
                                	//zwb/ cout << "similar image id: " << t <<", name: " << retrival_name[t]<<" ";
                                	ss << i << " " << retrival_name[t]<<" "<< scores.at<float>(i,t) << endl;
                      			 
					//}
				}
				

				else if(retrival_name[t] == "f2ac2484802d16ad761226c8db5f3a6d" || retrival_name[t] == "31be1bb7daedbd2098be93777da4e80a" || retrival_name[t] == "a1aac9e96c5c49dfabb6be5f428d100d" || retrival_name[t] == "eb886f4a465de7a32d7f8e353a73f99c" || retrival_name[t]== "a25967217571e914a97449b4fbeef812" || retrival_name[t] == "a1aac9e96c5c49dfabb6be5f428d100d" || retrival_name[t] == "252873714b9a08cdba77e27695707f38" || retrival_name[t] == "764115d430662f7943eaaec7db12dc3e" || retrival_name[t] == "11be01413909989811131a0193894914" || retrival_name[t] =="12488ec380d041534f82dd6cfeebccdf" || retrival_name[t] == "9f71e451fd044385478d2109ccb245e6" || retrival_name[t] == "b32a1a5cb98f07e3fb7c7c2542ed550d" || retrival_name[t] == "1d1339e7ee191ec94442595a5a5211e1" || retrival_name[t] == "26b69573ba74e9803319913f80171c61" || retrival_name[t] == "826e0ed562c5e1b842065ea59aef046b")
				{	
					if(scores.at<float>(i,t) > 0.93 && is_image[i])
					{
  						ss << i << " " << retrival_name[t]<<" "<< scores.at<float>(i,t) << endl;
  						cout << i << " " << retrival_name[t]<<" "<< scores.at<float>(i,t) << endl;
					}
				}	

				else
				{
					if (scores.at<float>(i,t) > threshold && is_image[i])
					{
					ss << i << " " << retrival_name[t]<<" "<< scores.at<float>(i,t) << endl;
					cout << i << " " << retrival_name[t]<<" "<< scores.at<float>(i,t) << endl;
					}		
				}
			 }
                     
                }
                time_t t6 = time(NULL);
				scores.release();
				index.release();
				return ss.str();
                //zwb/ cout << "show results done, time: " << (t6- t4) << " seconds."<< endl;		
}

void cosine_retrival_faiss(faiss::IndexFlatIP index,vector<bool> is_image,vector<string> query_name, vector<string> retrival_name, int queryNum, int retrivalNum, Mat feat_query, Mat feat_retrival, float threshold)
{
				
		//float* queryArr=new float[feat_query.rows*feat_query.cols];
		float *queryArr=(float*)feat_query.data;
		int TOPK = 1;
		long *I = new long[TOPK * queryNum];
		float *D = new float[TOPK * queryNum];
		index.search(queryNum, queryArr, TOPK, D, I);
		/*for(int i = 0; i < 5; i++) {
				for(int j = 0; j < TOPK; j++)
					printf("%5ld ", I[i * TOPK + j]);
            printf("\n");
		}*/

		for(int i = 0; i < queryNum; i++)
		{
				//zwb/ cout << endl << "query image id: "<< i << ", name: " << query_name[i] << endl;
				 
			for(int j = 0; j < TOPK; j++)
			{
				int t = I[i*TOPK+j];
				if (retrival_name[t] == "43c9c4b57e6c7e1638438c722b7fa927" || retrival_name[t] == "47b9fbd0d7258bbf45cc6c2fb9cd61e0" || retrival_name[t] == "8047b4e3cc6856335d15a4f96cd2e6a7" || retrival_name[t] == "982bcb887525dec8e8f84bdf08fd3b9f" || retrival_name[t] == "91b19e7d8c61cf0ae2ad97d1b7a962c4"|| retrival_name[t] == "e6cd78f4511152c9bc89ca185374ca7b" || retrival_name[t] == "e0fc1347f0245e75266177becc99b47c" || retrival_name[t] == "e5f3b10fe7a6e41dcae9d7faf571637c" || retrival_name[t] == "e09fcac5602b45916eb87602daf27c54" || retrival_name[t] == "c670e9e675272142e8c0ad9613e66192" || retrival_name[t] == "de03657e1c139cc39816a43704dd2932" || retrival_name[t] == "4fabfc3b2f28d1b6f8c3b617d617d249" || retrival_name[t] == "6e38d392766e64f8f75edde0c6776c98" || retrival_name[t] == "e4bb7fed959444c4d0b297ac21355f27" || retrival_name[t] == "874669bc3a775a0db66055250448ee8e" || retrival_name[t] == "9c49ef9bfad450c34ff61bf08113d3b0")
				{
					if(D[i*TOPK+j] > 0.93 && is_image[i])

						cout << i << " " << retrival_name[t]<<" "<< D[i*TOPK+j] << endl;

				}			

				else if(retrival_name[t] == "f2ac2484802d16ad761226c8db5f3a6d" || retrival_name[t] == "31be1bb7daedbd2098be93777da4e80a" || retrival_name[t] == "a1aac9e96c5c49dfabb6be5f428d100d" || retrival_name[t] == "eb886f4a465de7a32d7f8e353a73f99c" || retrival_name[t]== "a25967217571e914a97449b4fbeef812" || retrival_name[t] == "a1aac9e96c5c49dfabb6be5f428d100d" || retrival_name[t] == "252873714b9a08cdba77e27695707f38" || retrival_name[t] == "764115d430662f7943eaaec7db12dc3e" || retrival_name[t] == "11be01413909989811131a0193894914" || retrival_name[t] =="12488ec380d041534f82dd6cfeebccdf" || retrival_name[t] == "9f71e451fd044385478d2109ccb245e6" || retrival_name[t] == "b32a1a5cb98f07e3fb7c7c2542ed550d" || retrival_name[t] == "1d1339e7ee191ec94442595a5a5211e1" || retrival_name[t] == "26b69573ba74e9803319913f80171c61" || retrival_name[t] == "826e0ed562c5e1b842065ea59aef046b")
				{	
					if(D[i*TOPK+j] > 0.93 && is_image[i])
					{
						//ss << i << " " << retrival_name[t]<<" "<< D[i*TOPK+j) << endl;
						cout << i << " " << retrival_name[t]<<" "<< D[i*TOPK+j] << endl;
					}
				}	
				else
				{
					if (D[i*TOPK+j] > threshold && is_image[i])
					{
						//ss << i << " " << retrival_name[t]<<" "<< D[i*TOPK+j) << endl;
						cout << i << " " << retrival_name[t]<<" "<< D[i*TOPK+j] << endl;
					}		
				}
			 }
                     
        }
        time_t t6 = time(NULL);
        delete [] I;
        delete [] D;
                //zwb/ cout << "show results done, time: " << (t6- t4) << " seconds."<< endl;		
}
#endif
